package cn.gyouzhe.utils;

import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;

/**
 * httpclient请求工具
 *
 * @author wangchuan
 * @datetime 2018/3/3 23:25
 */
@Slf4j
public final class Request {
    private HttpClient httpClient;

    private Request() {
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();


        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
        poolingHttpClientConnectionManager.setMaxTotal(100);
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20);
        httpClientBuilder.setConnectionManager(poolingHttpClientConnectionManager);
        httpClientBuilder.setSSLSocketFactory(sslConnectionSocketFactory());

        List<Header> defaultHeaders = Arrays.asList(
                new BasicHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"),
                new BasicHeader("Accept-Encoding", "gzip, deflate"),
                new BasicHeader("Accept-Language", "zh-CN,zh;q=0.9"),
                new BasicHeader("Cache-Control", "max-age=0"),
                new BasicHeader("Upgrade-Insecure-Requests", "1"),
                new BasicHeader("Connection", "keep-alive"));
        httpClientBuilder.setDefaultHeaders(defaultHeaders);

        httpClientBuilder.setDefaultRequestConfig(requestConfig());
        httpClientBuilder.setRetryHandler(new StandardHttpRequestRetryHandler(1, false));
        httpClientBuilder.setUserAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
        httpClient = httpClientBuilder.build();

    }

    private static class InstanceHolder {
        static Request instance = new Request();
    }

    public static Request getInstance() {
        return InstanceHolder.instance;
    }

    /**
     * 请求配置
     * @return 请求配置对象
     */
    private RequestConfig requestConfig() {
        return RequestConfig.custom()
                .setConnectTimeout(30 * 1000)
                .setConnectionRequestTimeout(30 * 1000)
                .setSocketTimeout(30 * 1000)
                .build();
    }

//    private SSLConnectionSocketFactory add() {
//        try {
//            SSLContext context = SSLContext.getInstance("SSL");
//// 初始化
//            TrustManager[] tm = {new X509TrustManager() {
//                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
//                    return null;
//                }
//
//                public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
//                }
//
//                public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
//                }
//            }};
//
//            context.init(null, tm, new SecureRandom());
//            return context.getSocketFactory();
//        } catch(Exception e) {
//            e.printStackTrace();
//        }
//        return null;
//    }

    /**
     * 创建SSLContext
     * @return context
     */
    private SSLConnectionSocketFactory sslConnectionSocketFactory() {
        SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
        try {
            sslContextBuilder.loadTrustMaterial(null, (chain, authType) -> true);
            return new SSLConnectionSocketFactory(sslContextBuilder.build());
        } catch(NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
            log.error("处理SSL异常", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行请求
     * @param request 请求
     * @return 结果
     */
    private Response excuteRequest(HttpRequestBase request) {
        try {
            HttpResponse response = httpClient.execute(request);
            int code = response.getStatusLine().getStatusCode();
            String content = EntityUtils.toString(response.getEntity(), "UTF-8");
            return Response.of(code, content);
        } catch(IOException e) {
            log.error(request.getMethod() + "请求地址" + request.getURI() + "异常", e);
        } finally {
            request.reset();
        }
        return Response.of(500, null);
    }

    /**
     * 执行GET请求
     * @param url 请求地址
     * @param requestConfig 请求配置
     * @return 请求结果
     */
    private Response get(String url, RequestConfig requestConfig) {
        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(requestConfig);
        return excuteRequest(httpGet);
    }

    /**
     * 代理请求
     * @param url   请求URL
     * @param host  代理host
     * @param port  代理端口
     * @return 请求返回
     */
    public Response get(String url, String scheme, String host, int port) {
        HttpGet httpGet = new HttpGet(url);
        RequestConfig requestConfig = RequestConfig.copy(requestConfig())
                .setProxy(new HttpHost(host, port, scheme))
                .build();
        httpGet.setConfig(requestConfig);
        return excuteRequest(httpGet);
    }

    /**
     * 执行GET请求
     * @param url 请求地址
     * @return 请求返回
     */
    public Response get(String url) {
        return get(url, requestConfig());
    }

    /**
     * 返回结果
     */
    @Getter
    @ToString
    public static class Response {
        private int code;
        private String content;

        private Response(int code, String content) {
            this.code = code;
            this.content = content;
        }

        public int getCode() {
            return code;
        }

        public String getContent() {
            return content;
        }

        public static Response of(int code, String content) {
            return new Response(code, content);
        }
    }
}
